<?php

namespace App\Extends;

use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator;

abstract class BaseDao
{
    protected $model;

    public function __construct(Model $model)
    {
        $this->model = $model;
    }

    /**
     * 分页获取
     * @param array $where
     * @param int $limit
     * @param array $columns
     * @param string $pageName
     * @param int $page
     * @return LengthAwarePaginator
     */
    public function paginate(array $where, int $limit, array $columns, string $pageName, int $page): LengthAwarePaginator
    {
        $query = $this->model->newQuery();

        $this->applyConditions($query, $where);

        return $query->paginate($limit, $columns, $pageName, $page);
    }

    /**
     * 获取全部数据
     * @param string $field
     * @param string $type
     * @return Collection
     */
    public function all(string $field = 'id', string $type = 'asc')
    {
        return $this->model->orderBy($field, $type)->get();
    }

    /**
     * 根据主键查找记录
     * @param $id
     * @param array $columns
     * @return mixed
     */
    public function find($id, array $columns = ['*'])
    {
        return $this->model->select($columns)->find($id);
    }

    /**
     * 通用查询
     * @param array $andConditions
     * @param array $orConditions
     * @param array $columns
     * @param bool $singleRecord
     * @param string|null $orderBy
     * @param string $sortBy
     * @param callable|null $callback
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Builder[]|Collection|Model|object|null
     */
    public function findByConditions(
        array $andConditions = [],
        array $orConditions = [],
        array $columns = ['*'],
        bool $singleRecord = false,
        ?string $orderBy = null,
        string $sortBy = 'asc',
        ?callable $callback = null
    ) {
        $query = $this->model;

        // AND 条件
        $this->applyConditions($query, $andConditions);

        // OR 条件
        if (!empty($orConditions)) {
            $query->where(function ($q) use ($orConditions) {
                $this->applyConditions($q, $orConditions);
            });
        }

        // 排序
        if ($orderBy) {
            $query->orderBy($orderBy, $sortBy);
        }

        // 通过回调应用附加条件
        if ($callback) {
            $callback($query);
        }

        // 选择列并执行查询
        $query->select($columns);

        // 根据 $singleRecord 的值决定返回单个记录还是多个记录
        return $singleRecord ? $query->first() : $query->get();
    }

    /**
     * 将条件应用于查询
     * @param $query
     * @param array $conditions
     * @return void
     */
    protected function applyConditions($query, array $conditions)
    {
        foreach ($conditions as $condition) {
            if (is_array($condition)) {
                $this->applyCondition($query, ...$condition);
            }
        }
    }

    /**
     * 将单个条件应用于查询
     * @param $query
     * @param mixed ...$args
     * @return void
     */
    protected function applyCondition($query, ...$args)
    {
        $count = count($args);

        if ($count === 2) {
            // 处理 ['column', 'value'] 格式
            $column = $args[0];
            $value = $args[1];
            $query->where($column, '=', $value);
        } elseif ($count === 3) {
            // 处理 ['column', 'operator', 'value'] 格式
            $column = $args[0];
            $operator = $args[1];
            $value = $args[2];

            switch ($operator) {
                case '=':
                case '>':
                case '<':
                case '>=':
                case '<=':
                case '!=':
                    $query->where($column, $operator, $value);
                    break;
                case 'in':
                    $query->whereIn($column, $value);
                    break;
                case 'not in':
                    $query->whereNotIn($column, $value);
                    break;
                case 'like':
                    $query->where($column, 'like', $value);
                    break;
                case 'not like':
                    $query->where($column, 'not like', $value);
                    break;
                case 'between':
                    $query->whereBetween($column, $value);
                    break;
                case 'not between':
                    $query->whereNotBetween($column, $value);
                    break;
                default:
                    throw new \InvalidArgumentException("Unsupported operator: {$operator}");
            }
        } elseif ($count === 2 && is_array($args[1])) {
            // 处理 ['column', ['operator', $value]] 格式
            $column = $args[0];
            $operatorArray = $args[1];
            $operator = $operatorArray[0];
            $value = $operatorArray[1];

            switch ($operator) {
                case 'in':
                    $query->whereIn($column, $value);
                    break;
                case 'not in':
                    $query->whereNotIn($column, $value);
                    break;
                case 'between':
                    $query->whereBetween($column, $value);
                    break;
                case 'not between':
                    $query->whereNotBetween($column, $value);
                    break;
                default:
                    throw new \InvalidArgumentException("Unsupported operator: {$operator}");
            }
        } else {
            throw new \InvalidArgumentException("Invalid condition format: " . json_encode($args));
        }
    }

    /**
     * 获取某一列
     * @param array $criteria
     * @param string $field
     * @return array
     */
    public function findColumns(array $criteria, string $field)
    {
        $query = $this->model;

        $this->applyConditions($query, $criteria);

        // 执行查询并返回结果
        return $query->pluck($field);
    }

    /**
     * 新增一条数据
     * @param array $data
     * @return mixed
     */
    // public function create(array $data)
    // {
    //     return $this->model->create($data);
    // }

    // /**
    //  * 根据ID更新某一条数据
    //  * @param $id
    //  * @param array $data
    //  * @return mixed
    //  */
    // public function update($id, array $data)
    // {
    //     $model = $this->find($id);
    //     // 更新模型
    //     $model->update($data);
    //     return $model;
    // }
    /**
     * 新增一条或多条数据
     *
     * @param array $data 数据数组
     * @return mixed 返回创建的模型实例或布尔值
     */
    public function create(array $data)
    {
        // 如果 $data 是一个二维数组，则认为是批量插入
        if (isset($data[0]) && is_array($data[0])) {
            // 批量插入
            return $this->model->insert($data);
        } else {
            // 单条插入
            return $this->model->create($data);
        }
    }

    /**
     * 更新一条或多条数据
     *
     * @param mixed $id 单个 ID 或多个 ID 的数组
     * @param array $data 更新的数据数组
     * @param array|null $whereConditions 可选的条件数组
     * @return mixed 返回更新后的模型实例或受影响的行数
     */
    public function update($id, array $data, array $whereConditions = null)
    {
        // 如果 $id 是数组，则认为是批量更新
        if (is_array($id)) {
            // 批量更新
            return $this->model->whereIn('id', $id)->update($data);
        } elseif ($whereConditions) {
            // 根据条件批量更新
            return $this->model->where($whereConditions)->update($data);
        } else {
            // 单条更新
            $model = $this->find($id);
            if (!$model) {
                throw new \Exception("Model with ID {$id} not found");
            }
            $model->update($data);
            return $model;
        }
    }

    /**
     * 批量插入或更新
     * upsert 方法允许你根据唯一键（如 id 或 email）来决定是插入新记录还是更新现有记录
     */
    public function upsert(array $data, array $uniqueBy, array $updateColumns = null)
    {
        return $this->model->upsert($data, $uniqueBy, $updateColumns);
    }

    /**
     * 根据ID删除某一条数据
     * @param $id
     * @return void
     */
    public function delete($id)
    {
        $model = $this->find($id);
        return $model->delete();
    }

    /**
     * 修改状态
     * @param int $id
     * @return mixed
     */
    public function setStatus(int $id)
    {
        $model = $this->find($id);
        $model->status = !$model->status;
        $model->save();
        return $model;
    }
}